/**
 * 代码语法高亮工具
 * 支持多种编程语言的语法高亮
 */

/**
 * 根据技术栈获取关键字列表
 */
const getKeywordsByTech = (tech: string): string[] => {
	const keywordMap: Record<string, string[]> = {
		'java': [
			'public', 'private', 'protected', 'static', 'final', 'class', 'interface',
			'extends', 'implements', 'import', 'package', 'return', 'void', 'int',
			'long', 'double', 'float', 'boolean', 'String', 'if', 'else', 'for',
			'while', 'do', 'switch', 'case', 'break', 'continue', 'try', 'catch',
			'finally', 'throw', 'throws', 'new', 'this', 'super', 'abstract',
			'List', 'Map', 'Set', 'Optional', 'Stream', 'byte', 'short', 'char'
		],
		'javascript': [
			'var', 'const', 'let', 'function', 'async', 'await', 'export', 'import',
			'default', 'from', 'as', 'typeof', 'instanceof', 'delete', 'in', 'of',
			'null', 'true', 'false', 'undefined', 'NaN', 'Infinity', 'if', 'else',
			'for', 'while', 'do', 'switch', 'case', 'break', 'continue', 'try', 'catch',
			'finally', 'throw', 'return', 'new', 'this', 'class', 'extends',
			'Array', 'Object', 'Promise', 'Map', 'Set'
		],
		'typescript': [
			'var', 'const', 'let', 'function', 'async', 'await', 'export', 'import',
			'default', 'from', 'as', 'typeof', 'instanceof', 'delete', 'in', 'of',
			'null', 'true', 'false', 'undefined', 'NaN', 'Infinity', 'if', 'else',
			'for', 'while', 'do', 'switch', 'case', 'break', 'continue', 'try', 'catch',
			'finally', 'throw', 'return', 'new', 'this', 'class', 'extends',
			'type', 'interface', 'enum', 'namespace', 'declare', 'readonly',
			'any', 'unknown', 'never', 'number', 'string', 'boolean',
			'Array', 'Object', 'Promise', 'Map', 'Set'
		],
		'vue3': [
			'ref', 'reactive', 'computed', 'watch', 'onMounted', 'onUnmounted',
			'defineProps', 'defineEmits', 'nextTick', 'provide', 'inject',
			'const', 'let', 'var', 'function', 'async', 'await', 'export', 'import',
			'type', 'interface', 'enum'
		],
		'sql': [
			'SELECT', 'FROM', 'WHERE', 'INSERT', 'INTO', 'UPDATE', 'DELETE',
			'CREATE', 'DROP', 'ALTER', 'TABLE', 'INDEX', 'VIEW', 'DATABASE',
			'JOIN', 'LEFT', 'RIGHT', 'INNER', 'OUTER', 'ON', 'AND', 'OR', 'NOT',
			'ORDER', 'BY', 'GROUP', 'HAVING', 'LIMIT', 'OFFSET', 'AS', 'DISTINCT',
			'COUNT', 'SUM', 'AVG', 'MAX', 'MIN', 'NULL', 'IS', 'LIKE', 'IN',
			'VALUES', 'SET', 'PRIMARY', 'KEY', 'FOREIGN', 'REFERENCES', 'UNIQUE',
			'DEFAULT', 'AUTO_INCREMENT', 'VARCHAR', 'BIGINT',
			'DATETIME', 'TIMESTAMP', 'TEXT', 'BLOB', 'DECIMAL'
		],
		'mysql': [
			'SELECT', 'FROM', 'WHERE', 'INSERT', 'INTO', 'UPDATE', 'DELETE',
			'CREATE', 'DROP', 'ALTER', 'TABLE', 'INDEX', 'VIEW', 'DATABASE',
			'JOIN', 'LEFT', 'RIGHT', 'INNER', 'OUTER', 'ON', 'AND', 'OR', 'NOT',
			'ORDER', 'BY', 'GROUP', 'HAVING', 'LIMIT', 'OFFSET', 'AS', 'DISTINCT',
			'COUNT', 'SUM', 'AVG', 'MAX', 'MIN', 'NULL', 'IS', 'LIKE', 'IN',
			'VALUES', 'SET', 'PRIMARY', 'KEY', 'FOREIGN', 'REFERENCES', 'UNIQUE',
			'DEFAULT', 'AUTO_INCREMENT', 'VARCHAR', 'BIGINT', 'INT',
			'DATETIME', 'TIMESTAMP', 'TEXT', 'BLOB', 'DECIMAL', 'ENGINE', 'CHARSET'
		],
		'spring': [
			'public', 'private', 'protected', 'static', 'final', 'class', 'interface',
			'extends', 'implements', 'import', 'package', 'return', 'void',
			'String', 'if', 'else', 'for', 'while', 'try', 'catch', 'new', 'this'
		],
		'mybatis': [
			'public', 'private', 'protected', 'static', 'final', 'class', 'interface',
			'extends', 'implements', 'import', 'package', 'return', 'void',
			'String', 'if', 'else', 'for', 'while', 'try', 'catch', 'new', 'this'
		],
		'mybatisplus': [
			'public', 'private', 'protected', 'static', 'final', 'class', 'interface',
			'extends', 'implements', 'import', 'package', 'return', 'void',
			'String', 'if', 'else', 'for', 'while', 'try', 'catch', 'new', 'this'
		],
		'redis': [
			'GET', 'SET', 'DEL', 'EXISTS', 'EXPIRE', 'TTL', 'KEYS', 'SCAN',
			'HGET', 'HSET', 'HDEL', 'HGETALL', 'HMGET', 'HMSET', 'HSCAN',
			'LPUSH', 'RPUSH', 'LPOP', 'RPOP', 'LRANGE', 'LLEN', 'LINDEX',
			'SADD', 'SREM', 'SMEMBERS', 'SISMEMBER', 'SCARD', 'SSCAN',
			'ZADD', 'ZREM', 'ZRANGE', 'ZRANK', 'ZSCORE', 'ZCARD', 'ZINCRBY',
			'PUBLISH', 'SUBSCRIBE', 'PSUBSCRIBE', 'UNSUBSCRIBE', 'PUBSUB',
			'MULTI', 'EXEC', 'DISCARD', 'WATCH', 'UNWATCH',
			'PING', 'ECHO', 'INFO', 'CONFIG', 'FLUSHDB', 'FLUSHALL'
		],
		'docker': [
			'docker', 'run', 'build', 'pull', 'push', 'start', 'stop', 'restart',
			'ps', 'images', 'rm', 'rmi', 'exec', 'logs', 'inspect', 'network',
			'volume', 'compose', 'up', 'down', 'scale', 'FROM', 'RUN', 'CMD',
			'EXPOSE', 'ENV', 'WORKDIR', 'COPY', 'ADD', 'ENTRYPOINT', 'VOLUME'
		]
	};

	return keywordMap[tech] || keywordMap['java']; // 默认使用Java关键字
};

/**
 * 根据技术栈获取注解列表
 */
const getAnnotationsByTech = (tech: string): string[] => {
	// Java系列技术栈使用注解
	if (['java', 'spring', 'mybatis', 'mybatisplus'].includes(tech)) {
		return [
			'@Override', '@Autowired', '@Service', '@Component', '@Repository',
			'@Configuration', '@Bean', '@Value', '@Qualifier', '@Primary',
			'@Transactional', '@Async', '@Scheduled', '@EnableAsync',
			'@GetMapping', '@PostMapping', '@PutMapping', '@DeleteMapping',
			'@RequestMapping', '@PathVariable', '@RequestParam', '@RequestBody',
			'@ResponseBody', '@RestController', '@Controller', '@CrossOrigin',
			'@Mapper', '@Select', '@Insert', '@Update', '@Delete', '@Param',
			'@TableName', '@TableId', '@TableField'
		];
	}
	return [];
};

/**
 * 判断是否需要处理注释
 */
const shouldProcessComments = (tech: string): { multiLine: boolean, singleLine: boolean, sql: boolean, shell: boolean } => {
	return {
		multiLine: ['java', 'javascript', 'typescript', 'vue3', 'spring', 'mybatis', 'mybatisplus'].includes(tech),
		singleLine: ['java', 'javascript', 'typescript', 'vue3', 'spring', 'mybatis', 'mybatisplus'].includes(tech),
		sql: ['sql', 'mysql'].includes(tech),
		shell: ['docker'].includes(tech)
	};
};

/**
 * 代码语法高亮（根据techCode选择对应语言）
 * 
 * @param {string} code - 原始代码
 * @param {string} techCode - 技术栈代码（java/sql/redis等）
 * @returns {string} 高亮后的HTML
 */
export const highlightCode = (code: string, techCode: string = 'java'): string => {
	if (!code) return '';
	
	// 转义HTML特殊字符
	let highlighted = code
		.replace(/&/g, '&amp;')
		.replace(/</g, '&lt;')
		.replace(/>/g, '&gt;');
	
	// 使用占位符标记，避免重复高亮
	// 使用特殊的Unicode字符作为标记，避免与代码内容冲突
	const MARKER_START = '\u0001MARK_';
	const MARKER_END = '_MARK\u0002';
	let markerIndex = 0;
	const markers: { [key: string]: string } = {};
	
	// 辅助函数：创建标记
	const createMarker = (content: string): string => {
		const marker = `${MARKER_START}${markerIndex++}${MARKER_END}`;
		markers[marker] = content;
		return marker;
	};

	// 根据techCode确定需要处理的注释类型
	const commentTypes = shouldProcessComments(techCode);
	
	// 1. 先处理注释（优先级最高，避免注释内容被高亮）
	if (commentTypes.multiLine) {
		// 多行注释 /* */
		highlighted = highlighted.replace(/\/\*([\s\S]*?)\*\//g, (match) => {
			return createMarker(`<span style="color: #5c6370; font-style: italic;">${match}</span>`);
		});
	}
	
	if (commentTypes.singleLine) {
		// 单行注释 //
		highlighted = highlighted.replace(/\/\/(.*?)$/gm, (match) => {
			return createMarker(`<span style="color: #5c6370; font-style: italic;">${match}</span>`);
		});
	}
	
	if (commentTypes.sql) {
		// SQL注释 --
		highlighted = highlighted.replace(/^(--.*?)$/gm, (match) => {
			return createMarker(`<span style="color: #5c6370; font-style: italic;">${match}</span>`);
		});
	}
	
	if (commentTypes.shell) {
		// Shell注释 #
		highlighted = highlighted.replace(/^(#.*?)$/gm, (match) => {
			return createMarker(`<span style="color: #5c6370; font-style: italic;">${match}</span>`);
		});
	}
	
	// 2. 处理字符串（优先级第二）
	// 双引号字符串（包括跨行的情况）
	highlighted = highlighted.replace(/"([^"]*?)"/g, (match) => {
		return createMarker(`<span style="color: #98c379;">${match}</span>`);
	});
	// 单引号字符串
	highlighted = highlighted.replace(/'([^']*?)'/g, (match) => {
		return createMarker(`<span style="color: #98c379;">${match}</span>`);
	});
	// 反引号字符串
	highlighted = highlighted.replace(/`([^`]*?)`/g, (match) => {
		return createMarker(`<span style="color: #98c379;">${match}</span>`);
	});
	
	// 3. 处理数字
	highlighted = highlighted.replace(/\b(\d+\.?\d*[fLdD]?)\b/g, (match, p1) => {
		return createMarker(`<span style="color: #d19a66;">${p1}</span>`);
	});
	
	// 4. 处理注解（根据techCode）
	const annotations = getAnnotationsByTech(techCode);
	if (annotations.length > 0) {
		annotations.forEach(annotation => {
			const regex = new RegExp(`(${annotation.replace('@', '\\@')})\\b`, 'g');
			highlighted = highlighted.replace(regex, (match, p1) => {
				return createMarker(`<span style="color: #e5c07b; font-weight: 600;">${p1}</span>`);
			});
		});
	}
	
	// 5. 处理关键字（根据techCode选择对应语言的关键字）
	const keywords = getKeywordsByTech(techCode);
	
	// 根据不同技术栈使用不同颜色
	let keywordColor = '#c678dd'; // 默认紫色
	if (['sql', 'mysql'].includes(techCode)) {
		keywordColor = '#56b6c2'; // SQL使用青色
	} else if (techCode === 'redis') {
		keywordColor = '#e06c75'; // Redis使用红色
	} else if (techCode === 'docker') {
		keywordColor = '#61afef'; // Docker使用蓝色
	}
	
	keywords.forEach(keyword => {
		const regex = new RegExp(`\\b(${keyword})\\b`, 'g');
		highlighted = highlighted.replace(regex, (match, p1) => {
			return createMarker(`<span style="color: ${keywordColor}; font-weight: 600;">${p1}</span>`);
		});
	});
	
	// 6. 处理方法名/函数名
	highlighted = highlighted.replace(/\b([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/g, (match, p1) => {
		return createMarker(`<span style="color: #61afef;">${p1}</span>`) + '(';
	});
	
	// 7. 处理XML/HTML标签名（只高亮标签名，不处理属性部分，因为属性中的字符串已被保护）
	highlighted = highlighted.replace(/&lt;(\/?[a-zA-Z][a-zA-Z0-9-]*)/g, (match, p1) => {
		return `<span style="color: #e06c75;">&lt;${p1}</span>`;
	});
	// 处理标签结束符
	highlighted = highlighted.replace(/\/?&gt;/g, (match) => {
		return `<span style="color: #e06c75;">${match}</span>`;
	});
	
	// 8. 处理配置文件的键
	highlighted = highlighted.replace(/^([a-zA-Z][a-zA-Z0-9._-]*)\s*[:=]/gm, (match, p1) => {
		return createMarker(`<span style="color: #e06c75; font-weight: 600;">${p1}</span>`) + ':';
	});
	
	// 9. 还原所有标记（使用字符串替换而非正则，避免特殊字符问题）
	Object.keys(markers).forEach(marker => {
		// 使用split和join方式替换，避免正则特殊字符问题
		highlighted = highlighted.split(marker).join(markers[marker]);
	});
	
	// 10. 保持换行和空格
	highlighted = highlighted.replace(/\n/g, '<br/>');
	highlighted = highlighted.replace(/  /g, '&nbsp;&nbsp;');
	highlighted = highlighted.replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;');
	
	return `<div style="font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace; font-size: 26rpx; color: #f7fafc; line-height: 1.9; white-space: pre; padding-left: 20rpx; font-weight: 500;">${highlighted}</div>`;
};
